package com.qyer.search.servlet;

import static com.qyer.commons.utils.CommonUtils.cast2Boolelan;
import static com.qyer.commons.utils.CommonUtils.cast2Int;
import static com.qyer.commons.utils.CommonUtils.cast2String;
import static com.qyer.log.WarningLogger.logWarning;
import static com.qyer.search.enums.SKUType1.AIR_HOTEL;
import static com.qyer.search.enums.SKUType1.AIR_TICKETS;
import static com.qyer.search.enums.SKUType1.CAR_RENTAL;
import static com.qyer.search.enums.SKUType1.HOTEL;
import static com.qyer.search.enums.SKUType1.INSURANCE;
import static com.qyer.search.enums.SKUType1.LOCAL;
import static com.qyer.search.enums.SKUType1.SHIP;
import static com.qyer.search.enums.SKUType1.VISA;
import static com.qyer.search.enums.SearchError.INVALID_PARAMETER;
import static com.qyer.search.enums.SearchError.PLAN_FAILED;
import static com.qyer.search.enums.SearchError.RESULT_PROCESSING_ERROR;
import static com.qyer.search.enums.SearchError.SEARCH_FAILED;
import static com.qyer.search.model.result.SearchMetaInfo.EMPTY_SEARCH_META_INFO;
import static com.qyer.search.searchplan.SQPlanner.makePlan;
import static com.qyer.search.utils.ModelUtils.packageAll;
import static com.qyer.search.utils.ModelUtils.packageByType1;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qyer.commons.blender.Blender;
import com.qyer.commons.blender.BlenderLayoutPattern;
import com.qyer.commons.blender.keyconverter.StringIntConverter;
import com.qyer.commons.exception.InvocationException;
import com.qyer.log.ErrorLogger;
import com.qyer.log.PhraseShowLogger;
import com.qyer.log.TimeCostLogger;
import com.qyer.log.WarningLogger;
import com.qyer.search.Constants;
import com.qyer.search.DeserializerProcessor.DeserializerProcessor;
import com.qyer.search.DeserializerProcessor.DeserializerProcessorWithNoType;
import com.qyer.search.annotation.TemplateName;
import com.qyer.search.cache.mutable.MutableCaches;
import com.qyer.search.engine.ForkJoinESEngine;
import com.qyer.search.exception.SearchException;
import com.qyer.search.exception.InvalidParameterException;
import com.qyer.search.filter.AbstractFilter;
import com.qyer.search.filter.ChannelFilter;
import com.qyer.search.filter.StartTimeFilter;
import com.qyer.search.model.SearchParameter;
import com.qyer.search.model.SearchParameterOrigin;
import com.qyer.search.model.lastminute.SKU;
import com.qyer.search.model.result.SearchMetaInfo;
import com.qyer.search.model.result.SearchPackage;
import com.qyer.search.model.result.Type1SearchPackage;
import com.qyer.search.report.ReporterQueue;
import com.qyer.search.report.SearchReportItem;
import com.qyer.search.report.TimeCost;
import com.qyer.search.searchplan.SQPlan;
import com.qyer.search.searchplan.SQPlanner;
import com.qyer.search.sort.PriceSorter;
import com.qyer.search.sort.SalesSorter;
import com.qyer.search.sort.Type1Sorter;
import com.qyer.search.template.VMTemplate;
import com.qyer.search.threading.DeserializerThreadPoolHolder;
import com.qyer.search.utils.ListUtils;
import com.qyer.search.utils.SplitStringUtils;
import com.qyer.search.utils.WordETLClient;
import com.qyer.words.model.Impression;
import com.qyer.words.model.Word;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.*;

import org.joda.time.*;
import org.joda.time.format.*;
import java.util.*;
/**
 * User: Z J Wu Date: 2015/12/18 Time: 11:00 Package: com.qyer.search.servlet
 */
@TemplateName("search_result.test.vm")
public class BasicSearchServlet extends AbstractServlet {

  private static final Logger LOGGER = LoggerFactory.getLogger(BasicSearchServlet.class);

  private static final List<BlenderLayoutPattern<Integer>> DEFAULT_BLENDING_LAYOUT = Lists
    .newArrayListWithCapacity(8);

  private static final String DEFAULT_STRA = "GMV-PRIORITY";

  static {
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(AIR_HOTEL.getId(), 3));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(AIR_TICKETS.getId()));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(LOCAL.getId()));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(HOTEL.getId()));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(SHIP.getId()));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(CAR_RENTAL.getId()));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(INSURANCE.getId()));
    DEFAULT_BLENDING_LAYOUT.add(new BlenderLayoutPattern<>(VISA.getId()));
  }

  private void report(SearchReportItem sri, TimeCost timeCost) throws JsonProcessingException {
    TimeCostLogger.logTimeCost(sri.getSearchParameter(), timeCost);
    try {
      ReporterQueue.getInstance().putToQueue(sri);
    } catch (InterruptedException e) {
      LOGGER.error("Interrupt reporting.", e);
    }
  }

  @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws
    ServletException, IOException {
    long beginTime = System.currentTimeMillis();
    resp.setContentType("text/html");
    resp.setCharacterEncoding("utf-8");

    SearchParameterOrigin searchParameterOrigin = null;
    try {
      searchParameterOrigin = new SearchParameterOrigin(req, resp, this);
    }catch (Exception e){
      //writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, e);
      return;
    }
    try {
      checkParam(searchParameterOrigin);
    } catch (Exception e) {
      writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, e);
      return;
    }

    // 使用简单结果(只有id)
    boolean useSimpleResult = cast2Boolelan(req.getParameter("simple"));
    // 展示模式(可视化界面 or Json数据)
    boolean onlyData = cast2Boolelan(req.getParameter("only_data"), true);
    // 混排模式(按照一级类目分开 or 混排)
    boolean blendAll = cast2Boolelan(req.getParameter("blend_all"), false);
    String blendString = cast2String(req.getParameter("blend_str"));

    boolean basic_data = cast2Boolelan(req.getParameter("basic"), false);

    boolean meta_data = cast2Boolelan(req.getParameter("meta_info"), false);

    //
    List<AbstractFilter> filters = new ArrayList<>();
    //
    String channels = cast2String(req.getParameter("channel"));
    if(StringUtils.isNotBlank(channels)){
      filters.add(new ChannelFilter(SplitStringUtils.splitTostrs(channels, Constants.SEPARATOR_PARAMETER)));
    }
    //出发时间筛选 yyyyMM
    long start = 0;
    long end = 0;
    String startStr = cast2String(req.getParameter("from"));
    String endStr = cast2String(req.getParameter("to"));
    try {
      if (!(StringUtils.isBlank(startStr) && StringUtils.isBlank(endStr))) {
        if (StringUtils.isBlank(startStr)) {
          start = new DateTime().millisOfDay().withMinimumValue().getMillis() / 1000;
          startStr = "今天";
        }
        else
          start = DateTimeFormat.forPattern("yyyyMMdd").parseDateTime(startStr).getMillis() / 1000;
        if (StringUtils.isBlank(endStr)) {
          end = Long.MAX_VALUE;
          endStr = "之后";
        }
        else {
          end = DateTimeFormat.forPattern("yyyyMMdd").parseDateTime(endStr).getMillis() / 1000;
          endStr = "---" + endStr;
        }

        if(start >= end)
          throw new InvalidParameterException("参数异常");
        else
          filters.add(new StartTimeFilter(start, end));
      }

    }catch (Exception e){
      writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, e);
      return;
    }
    String startTimeShow = "";

    if((startStr == null)&&(endStr == null))
      startTimeShow = "全部";
    else
      startTimeShow = startStr + endStr;

    int limit = Integer.MAX_VALUE;
    String limitStr = "全部";
    try {
      limitStr = req.getParameter("limit");
      if(limitStr != null) {
        //limit = cast2Int(limitStr, Integer.MAX_VALUE);
        limit = cast2Int(limitStr, Constants.DEFAULT_LIMIT);
        if(limit < 0) {
          writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, new InvalidParameterException("limit 为负"));
          return;
        }
      }
      else
        limitStr = "全部";
    }catch (Exception e){
      writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, e);
      return;
    }
    //排序选择
    String sortRegular;
    int sort = 0;
    try {
      sortRegular = req.getParameter("sort");
      sort = cast2Int(sortRegular, 0);
    }catch (Exception e){
      writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, e);
      return;
    }

    // 词汇处理
    long t1 = System.currentTimeMillis();
    String userInputKW = searchParameterOrigin.getUserInputKW();
    Set<List<Word>> tuples = null;
    try {
      tuples = WordETLClient.getInstance().
        getBasicWord(userInputKW, searchParameterOrigin.getRequestId());
    } catch (Exception e) {
      WarningLogger.logWarning("Cannot etl user input keywords.", searchParameterOrigin, e);
      Word word = new Word(userInputKW);
      try {
        PhraseShowLogger.logSplitResult("hmm", searchParameterOrigin.getRequestId(), "hanmeimeiError", word);
      } catch (Exception ee) {
        LOGGER.warn("Cannot send report({})", e);
      }
      List<Word> list = new ArrayList<>();
      list.add(word);
      tuples = new HashSet<>();
      tuples.add(list);

    }
    //System.out.println("分词时间:"+(System.currentTimeMillis() - t1));
    //searchParameterOrigin.addTypes(tuples);
    Set<Integer> preSKUType1 = new HashSet<>();
    Set<Integer> preSKUType2 = new HashSet<>();
    boolean local = false;

    boolean canDefinDest = true;
    int n_Continent = 0;
    int n_Country = 0;
    int n_City = 0;
    Word wordCon = null;
    Word wordCou = null;
    Word wordCit = null;
    //意图判断
    if(tuples.size() == 1){
      for(List<Word> list:tuples){
        if(list.size() == 1) {
          if ((list.get(0).getFirstImpression() == Impression.IMPRESSION_CITY)
            || (list.get(0).getFirstImpression() == Impression.IMPRESSION_COUNTRY
          )) {
            /*if(MutableCaches.getInstance().OVERSEA_CITIES.getCacheContent().containsKey(list.get(0).getName())
              || MutableCaches.getInstance().OVERSEA_COUNTRIES.getCacheContent().containsKey(list.get(0).getName())) {
              searchParameterOrigin.reconstruct(list.get(0).getName());
              tuples = new HashSet<>();
            }*/
            Set<Integer> type1s = searchParameterOrigin.getType1s();
            if(CollectionUtils.isNotEmpty(type1s)){
              if(type1s.contains(1017)){
                preSKUType1.add(1017);
              }
            }
            if(MutableCaches.getInstance().OVERSEA_CITIES.getCacheContent().containsKey(
              list.get(0).getName())){
              //System.out.println("意图识别:城市");
              searchParameterOrigin.reconstructWithCity(list.get(0).getName());
              tuples = new HashSet<>();
            }else if(MutableCaches.getInstance().OVERSEA_COUNTRIES.getCacheContent().containsKey(list.get(0).getName())){
              searchParameterOrigin.reconstructWithCountry(list.get(0).getName());
              tuples = new HashSet<>();
            }
          }
          else if(Constants.HOT_PS_1.containsKey(list.get(0).getName())){
            preSKUType1.add(Constants.HOT_PS_1.get(list.get(0).getName()));
          }else if(Constants.HOT_PS_2.containsKey(list.get(0).getName())){
            preSKUType2.add(Constants.HOT_PS_2.get(list.get(0).getName()));
          }
        }else {
          for(Word word: list){
            if(Constants.HOT_PS_1.containsKey(word.getName())){
              preSKUType1.add(Constants.HOT_PS_1.get(word.getName()));
            }else if(Constants.HOT_PS_2.containsKey(word.getName())){
              preSKUType2.add(Constants.HOT_PS_2.get(word.getName()));
            }
            //确定目的地
            if(canDefinDest) {
              if (word.getFirstImpression() == Impression.IMPRESSION_CONTINENT) {
                //System.out.println("大洲");
                if (++n_Continent > 1) {
                  canDefinDest = false;
                  break;
                } else {
                  wordCon = word;
                }
              }else if(word.getFirstImpression() == Impression.IMPRESSION_COUNTRY){
                //System.out.println("国家识别~");
                if (++n_Country > 1) {
                  canDefinDest = false;
                  break;
                } else {
                  wordCou = word;
                }
              }else if(word.getFirstImpression() == Impression.IMPRESSION_CITY){
                //System.out.println("城市识别~");
                if (++n_City > 1) {
                  canDefinDest = false;
                  break;
                } else {
                  wordCit = word;
                }
              }
            }
          }

          //是否一个地方
          if(canDefinDest){
            if((n_City + n_Country + n_Continent) > 1){
              if(wordCit == null){
                String _country = wordCou.getName();
                String _continent = wordCon.getName();
                //System.out.println("城市为空:" + "国家:"+_country + "：大洲:"+_continent);
                if(_continent.equals(MutableCaches.getInstance().COUNTRY_CONTINENT_MAP.getCacheContent().get(_country))){
                  searchParameterOrigin.setDestCountryIf(_country);
                  list.remove(wordCon);
                  list.remove(wordCou);
                  if(CollectionUtils.isEmpty(list)){
                    tuples.clear();
                  }
                }
              }else if(wordCou == null){
                String _city = wordCit.getName();
                String _continent = wordCon.getName();
                //System.out.println("国家为空:" + "国家:"+_city + "：大洲:"+_continent);
                List<String> _countryList = MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent().get(_city);
                if(CollectionUtils.isNotEmpty(_countryList)){
                  for(String _country : _countryList){
                    String continent1 = MutableCaches.getInstance().COUNTRY_CONTINENT_MAP.getCacheContent().get(_country);
                    if(_continent.equals(continent1)){
                      searchParameterOrigin.setDestCountryIf(_country);
                      searchParameterOrigin.setDestIf(_city);
                      list.remove(wordCon);
                      list.remove(wordCit);
                      if(CollectionUtils.isEmpty(list)){
                        tuples.clear();
                      }
                    }
                  }
                }
              }else if(wordCon == null){
                //System.out.println("大洲为空");
                String _city = wordCit.getName();
                String _country = wordCou.getName();
                //System.out.println("大洲为空:" + "国家:"+_city + "：国家:"+_country);
                List<String> _countryList = MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent().get(
                  _city);
                if(CollectionUtils.isNotEmpty(_countryList)) {
                  for (String str : _countryList) {
                    if(_country.equals(str)){
                      searchParameterOrigin.setDestCountryIf(_country);
                      searchParameterOrigin.setDestIf(_city);
                      list.remove(wordCit);
                      list.remove(wordCou);
                      if(CollectionUtils.isEmpty(list)){
                        tuples.clear();
                      }
                    }
                  }
                }
              }else {
                String _city = wordCit.getName();
                String _country = wordCou.getName();
                String _continent = wordCon.getName();
                List<String> _countryList = MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent().get(
                  _city);
                if(_continent.equals(MutableCaches.getInstance().COUNTRY_CONTINENT_MAP.getCacheContent().get(_country))) {
                  if (CollectionUtils.isNotEmpty(_countryList)) {
                    for (String str : _countryList) {
                      if (_country.equals(str)) {
                        searchParameterOrigin.setDestIf(_city);
                        searchParameterOrigin.setDestCountryIf(_country);
                        list.remove(wordCit);
                        list.remove(wordCou);
                        list.remove(wordCon);
                        if(CollectionUtils.isEmpty(list)){
                          tuples.clear();
                        }
                      }
                    }
                  }
                }
              }
            }else if(n_City == 1){
              String _city = wordCit.getName();
              if(MutableCaches.getInstance().CITY_COUNTRY_MAP_LIST.getCacheContent().containsKey(_city)){
                searchParameterOrigin.reconstructWithCity(_city);
                list.remove(wordCit);
              }else if(MutableCaches.getInstance().TAIWAN_MAP.getCacheContent().contains(_city)){
                searchParameterOrigin.reconstructWithCity(_city);
                list.remove(wordCit);
              }
            }
          }
        }
      }
    }else{
      for(List<Word> list: tuples){
        for(Word word: list){
          if(Constants.HOT_PS_1.containsKey(word.getName())){
            preSKUType1.add(Constants.HOT_PS_1.get(word.getName()));
          }else if(Constants.HOT_PS_2.containsKey(word.getName())){
            preSKUType2.add(Constants.HOT_PS_2.get(word.getName()));
          }
          //确定目的地
        }
      }
    }
    //
    long t2 = System.currentTimeMillis();
    //System.out.println("词分析时间:"+(t2-t1));
    // 构建搜索/查询Plan
    List<SQPlan> plans;
    //System.out.println(tuples);
    try {
      //plans = makePlan(searchParameterOrigin, tuples);
      //plans = SQPlanner.makePlanEx3(searchParameterOrigin, tuples);
      plans = SQPlanner.makePlanEx4(searchParameterOrigin, tuples);
    } catch (Exception e) {
      e.printStackTrace();
      writeError(searchParameterOrigin, resp, beginTime, PLAN_FAILED, e);
      return;
    }
    // 执行搜索
    long t3 = System.currentTimeMillis();
    //System.out.println("搜索计划时间:"+(t3-t2));

    // JsonString的反序列化工作交给App层面
    List<String> searchResultStringList;
    /*try {
      searchResultStringList = ForkJoinESEngine.getInstance()
                                               .forkSearch(searchParameterOrigin, plans, false,
                                                           useSimpleResult, basic_data);
    } catch (SearchException e) {
      writeError(searchParameterOrigin, resp, beginTime, SEARCH_FAILED, e);
      return;
    }*/
    //////////////////////////////////++++++++++++++++++++++++++
    try {
      searchResultStringList = ForkJoinESEngine.getInstance()
                                               .forkSearchThread(searchParameterOrigin, plans, false,
                                                           useSimpleResult, basic_data);
    } catch (SearchException e) {
      writeError(searchParameterOrigin, resp, beginTime, SEARCH_FAILED, e);
      return;
    }
    //////////////////////////////////++++++++++++++++++++++++++
    // 整理结果(必须按照type1整理, 因为目前排序是只能按照type1排序)
    long t4 = System.currentTimeMillis();
    //System.out.println("搜索时间:"+(t4 - t3));
    long t5 = 0;
    long t6 = 0;
    long t7 = 0;
    long transformMergeResultTime = 0;

    //SearchMetaInfo metaInfo = EMPTY_SEARCH_META_INFO;
    SearchMetaInfo metaInfo;
    List<Type1SearchPackage> packages = null;
    SearchPackage searchPackage = null;
    List<? extends SearchPackage> finalPackage = null;

    Map<Integer,Map<Integer,Integer>> placeSearchCensus = new HashMap<>();
    Map<Integer,Map<Integer,Integer>> typeSearchCensus = new HashMap<>();
    if(meta_data){
      metaInfo = new SearchMetaInfo(typeSearchCensus, placeSearchCensus);
    }else {
      metaInfo = EMPTY_SEARCH_META_INFO;
    }

    int searchResultSize = searchResultStringList.size();
    //System.out.println("searchSize:"+searchResultSize);
    int count = 0;
    int rest = 0;

    count = searchResultSize/Constants.SEARCH_THRESHOLD;
    rest = searchResultSize%Constants.SEARCH_THRESHOLD;

    //System.out.println("count:"+count);
    //System.out.println("rest:"+rest);
    if(rest > Constants.HALF_SEARCH_THRESHOLD){
      count++;
    }
    //System.out.println("after count:"+count);
    int count1 = count - 1;
    if(sort == 0) {
      if (count > 1) {
        //
        //System.out.println("使用线程池.");
        ExecutorService service = DeserializerThreadPoolHolder.getInstance().getService();
        List<Future<List<Type1SearchPackage>>> futureList = new ArrayList<>();
        List<String> stringList = null;
        List<Map<Integer, Map<Integer, Integer>>> placeSearchCensuses = new ArrayList<>();
        List<Map<Integer, Map<Integer, Integer>>> typeSearchCensuses = new ArrayList<>();
        //
        for (int i = 0; i < count; i++) {

          //long s11 = System.currentTimeMillis();
          if (i != count1) {
            stringList = searchResultStringList
              .subList(i * Constants.SEARCH_THRESHOLD, (i + 1) * Constants.SEARCH_THRESHOLD);
          } else {
            stringList = searchResultStringList
              .subList(i * Constants.SEARCH_THRESHOLD, searchResultSize);

          }
          //long s12 = System.currentTimeMillis();
          //System.out.println("线程启动前时间:" + (s12 - s11));
          Map<Integer, Map<Integer, Integer>> placeCensus = new HashMap<>();
          Map<Integer, Map<Integer, Integer>> typeCensus = new HashMap<>();
          DeserializerProcessor deserializerProcessor = new DeserializerProcessor(
            searchParameterOrigin, stringList, useSimpleResult, basic_data, meta_data,
            placeCensus, typeCensus, filters, System.currentTimeMillis());
          placeSearchCensuses.add(placeCensus);
          typeSearchCensuses.add(typeCensus);
          futureList.add(service.submit(deserializerProcessor));
        }
        //System.out.println("thread start time:"+(System.currentTimeMillis() - t4));
        //获得结果
        packages = new ArrayList<>();
        List<Type1SearchPackage> t1list;
        boolean isHit = false;
        for(int i = 0; i < count; i++){
          try {
            t1list = futureList.get(i).get(1000, TimeUnit.MILLISECONDS);
            int size_future = t1list.size();
            for(int j = 0; j < size_future; j++){
              Type1SearchPackage ttemp = t1list.get(j);
              int c1 = ttemp.getType1();
              //System.out.println("TYPE1:"+c1);
              for(int k = 0; k < packages.size(); k++) {
                Type1SearchPackage ttemp2 = packages.get(k);
                int c11 = ttemp2.getType1();
                //System.out.println("TY!:"+c11);
                if (c11 == c1) {
                  ttemp2.addSkus(ttemp.getSkus());
                  isHit = true;
                  //System.out.println("HIT:"+c1+",NEXT:"+c11);
                  break;
                } else {
                  //System.out.println("contine");
                  continue;
                }
              }
              if(!isHit){
                packages.add(ttemp);
              }
              isHit = false;
            }

            Iterator<Map.Entry<Integer, Map<Integer, Integer>>> iterator = placeSearchCensuses.get(i).entrySet().iterator();
            while (iterator.hasNext()) {
              Map.Entry<Integer, Map<Integer, Integer>> entry = iterator.next();
              int placeId = entry.getKey();
              if(placeSearchCensus.containsKey(placeId)){
                Map<Integer, Integer> map = entry.getValue();
                int tmp = map.get(-1);
                int tmp2 = placeSearchCensus.get(placeId).get(-1);
                placeSearchCensus.get(placeId).put(-1, tmp + tmp2);
              }else {
                placeSearchCensus.put(entry.getKey(), entry.getValue());
              }
            }
            //////////////////////////////////////////////////////////
            iterator = typeSearchCensuses.get(i).entrySet().iterator();
            while (iterator.hasNext()) {
              Map.Entry<Integer, Map<Integer, Integer>> entry = iterator.next();
              int typeId = entry.getKey();
              if(typeSearchCensus.containsKey(typeId)){
                Map<Integer, Integer> map = entry.getValue();
                int tmp = map.get(-1);
                int tmp2 = typeSearchCensus.get(typeId).get(-1);
                typeSearchCensus.get(typeId).put(-1, tmp + tmp2);
              }else {
                typeSearchCensus.put(entry.getKey(), entry.getValue());
              }
            }
            //////////////////////////////////////////////////////////
          }catch (Exception e){
            ErrorLogger.logError(searchParameterOrigin, e);
          }
        }
        //System.out.println("time:"+(System.currentTimeMillis() - t4));
        //

      }else {
        //System.out.println("未用线程池.");
        WarningLogger.logWarning("sort:0", searchParameterOrigin, null);
        long t12 = System.currentTimeMillis();
        if(meta_data){
          if (filters.size() == 0) {
            packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                      basic_data, placeSearchCensus, typeSearchCensus);
          } else {
            try {
              packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                        basic_data, filters, placeSearchCensus, typeSearchCensus);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }else {
          if (filters.size() == 0) {
            packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                      basic_data);
          } else {
            try {
              packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                        basic_data, filters);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }
        //System.out.println("检测序列化时间:" + (System.currentTimeMillis() - t12));
      }
      //排序
      t5 = System.currentTimeMillis();
      transformMergeResultTime = t5 - t4;
      //System.out.println("反序列化时间:"+transformMergeResultTime);

      // 排序
      long t11 = System.currentTimeMillis();
      List<Type1SearchPackage> sortedPackages;
      try {
        sortedPackages = new Type1Sorter(searchParameterOrigin, packages).sortSearchPackages();
        //sortedPackages = new Type1Sorter(searchParameterOrigin, packages).sortSearchPackagesWithThreads();
      } catch (SearchException e) {
        logWarning("Sorting failed", searchParameterOrigin, e);
        sortedPackages = packages;
      }
      t6 = System.currentTimeMillis();
      //System.out.println("排序时间:"+(t6 - t11));
      List<SKU> skuLists = new ArrayList<>();
      List<Type1SearchPackage> sortedPackages1 = new ArrayList<>();
      if (blendAll&& CollectionUtils.isNotEmpty(sortedPackages)) {
        //如果混排，将更符合用户意图的产品前置
        if(!preSKUType2.isEmpty()) {
          local = true;
        }
        for(int i = 0; i < sortedPackages.size(); i++){
          Type1SearchPackage type1SearchPackage = sortedPackages.get(i);
          if(preSKUType1.contains(type1SearchPackage.getType1())){
            skuLists.addAll(type1SearchPackage.getSkus());
          }else if(local && (type1SearchPackage.getType1() == 2410)){
            boolean isEmpty = true;
            Type1SearchPackage type1SearchPackage1 = new Type1SearchPackage();
            List<SKU> _skus = type1SearchPackage.getSkus();
            for(SKU sku:_skus){
              if(preSKUType2.contains(sku.getType2()))
                skuLists.add(sku);
              else {
                type1SearchPackage1.addSku(sku);
                isEmpty = false;
              }
            }
            if(!isEmpty){
              type1SearchPackage1.setType1(2410);
              sortedPackages1.add(type1SearchPackage1);
            }
          }else {
            sortedPackages1.add(type1SearchPackage);
          }
        }
        //
        int preSize = skuLists.size();
        List<SKU> skus = null;
        SearchPackage blendedSearchPackage;
        if(limit != Integer.MAX_VALUE)
          blendedSearchPackage = new SearchPackage(limit);
        else
          blendedSearchPackage = new SearchPackage(1000);
        if(preSize > limit){
          skus = ListUtils.ToSubList(skuLists, 0, limit);
        }else if((preSize < limit)&&CollectionUtils.isNotEmpty(sortedPackages1)){
          sortedPackages = sortedPackages1;
          Blender<Integer, SKU> blender;
          Map<Integer, List<SKU>> map = Maps.newHashMapWithExpectedSize(sortedPackages.size());
          for (Type1SearchPackage type1SearchPackage : sortedPackages) {
            map.put(type1SearchPackage.getType1(), type1SearchPackage.getSkus());
          }
          try {
            if (StringUtils.isEmpty(blendString)) {
              blender = new Blender<>(DEFAULT_BLENDING_LAYOUT, map);
            } else {
              blender = new Blender<>(blendString, 3, new StringIntConverter(), map);
            }
            blender.blend();
          } catch (Exception e) {
            writeError(searchParameterOrigin, resp, beginTime, RESULT_PROCESSING_ERROR, e);
            return;
          }
          //List<SKU> skus = blender.getTotalList();
          skus = skuLists;
          skus.addAll(ListUtils.ToSubList(blender.getTotalList(), 0, limit - preSize));
        }else {
          skus = skuLists;
        }
        //
        //blendedSearchPackage.addSkus(skuLists);
        blendedSearchPackage.addSkus(skus);
        finalPackage = Lists.newArrayList(blendedSearchPackage);
      } else {
        if(limit == Integer.MAX_VALUE)
          finalPackage = sortedPackages;
        else{
          for(int i = 0; i < sortedPackages.size(); i++){
            sortedPackages.get(i).resize(limit);
          }
          finalPackage = sortedPackages;
        }
      }
      //System.out.println("混排时间:"+(System.currentTimeMillis() - t6));
      //
    }else if(sort > 0 && sort < 5){
      if(count > 1){
        //////////////////////////////////////////////////////////////////
        ExecutorService service = DeserializerThreadPoolHolder.getInstance().getService();
        List<Future<SearchPackage>> futureList = new ArrayList<>();
        List<String> stringList = null;
        List<Map<Integer, Map<Integer, Integer>>> placeSearchCensuses = new ArrayList<>();
        List<Map<Integer, Map<Integer, Integer>>> typeSearchCensuses = new ArrayList<>();
        //
        for (int i = 0; i < count; i++) {
          //long s1 = System.currentTimeMillis();
          if (i != count1) {
            stringList = searchResultStringList
              .subList(i * Constants.SEARCH_THRESHOLD, (i + 1) * Constants.SEARCH_THRESHOLD);
          } else {
            stringList = searchResultStringList
              .subList(i * Constants.SEARCH_THRESHOLD, searchResultSize);

          }
          //long s2 = System.currentTimeMillis();
          //System.out.println("循环时间:" + (s2 - s1));

          Map<Integer, Map<Integer, Integer>> placeCensus = new HashMap<>();
          Map<Integer, Map<Integer, Integer>> typeCensus = new HashMap<>();
          DeserializerProcessorWithNoType deserializerProcessorWithNoType = new DeserializerProcessorWithNoType(
            searchParameterOrigin, stringList, useSimpleResult, basic_data, meta_data,
            placeCensus, typeCensus, filters, System.currentTimeMillis());
          placeSearchCensuses.add(placeCensus);
          typeSearchCensuses.add(typeCensus);
          futureList.add(service.submit(deserializerProcessorWithNoType));
        }
        //System.out.println("thread start time:"+(System.currentTimeMillis() - t4));
        //获得结果
        searchPackage = new SearchPackage();
        for(int i = 0; i < count; i++){
          try {
            searchPackage.addSkus(futureList.get(i).get().getSkus());
            Iterator<Map.Entry<Integer, Map<Integer, Integer>>> iterator = placeSearchCensuses.get(i).entrySet().iterator();
            while (iterator.hasNext()) {
              Map.Entry<Integer, Map<Integer, Integer>> entry = iterator.next();
              int placeId = entry.getKey();
              if(placeSearchCensus.containsKey(placeId)){
                Map<Integer, Integer> map = entry.getValue();
                int tmp = map.get(-1);
                int tmp2 = placeSearchCensus.get(placeId).get(-1);
                placeSearchCensus.get(placeId).put(-1, tmp + tmp2);
              }else {
                placeSearchCensus.put(entry.getKey(), entry.getValue());
              }
            }
            //////////////////////////////////////////////////////////
            iterator = typeSearchCensuses.get(i).entrySet().iterator();
            while (iterator.hasNext()) {
              Map.Entry<Integer, Map<Integer, Integer>> entry = iterator.next();
              int typeId = entry.getKey();
              if(typeSearchCensus.containsKey(typeId)){
                Map<Integer, Integer> map = entry.getValue();
                int tmp = map.get(-1);
                int tmp2 = typeSearchCensus.get(typeId).get(-1);
                typeSearchCensus.get(typeId).put(-1, tmp + tmp2);
              }else {
                typeSearchCensus.put(entry.getKey(), entry.getValue());
              }
            }
            //////////////////////////////////////////////////////////
          }catch (Exception e){
            ErrorLogger.logError(searchParameterOrigin, e);
          }
        }
        //System.out.println("time:"+(System.currentTimeMillis() - t4));
        //////////////////////////////////////////////////////////////////
      }else {
        if(meta_data){
          if (filters.size() == 0) {
            searchPackage = packageAll(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                       basic_data, placeSearchCensus, typeSearchCensus);
          } else {
            try {
              searchPackage = packageAll(searchParameterOrigin, searchResultStringList,
                                         useSimpleResult, basic_data, filters, placeSearchCensus, typeSearchCensus);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }else {
          if (filters.size() == 0) {
            searchPackage = packageAll(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                       basic_data);
          } else {
            try {
              searchPackage = packageAll(searchParameterOrigin, searchResultStringList,
                                         useSimpleResult, basic_data, filters);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }
      }
      //排序
      t5 = System.currentTimeMillis();
      //t6 = t5;
      transformMergeResultTime = t5 - t4;
      if(sort == 1){
        try {
          finalPackage = new SalesSorter(searchParameterOrigin, searchPackage, 0, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:1", searchParameterOrigin, null);
        }catch (Exception e){
          logWarning("Sorting failed", searchParameterOrigin, e);
          e.printStackTrace();
          finalPackage = Lists.newArrayList(searchPackage);
        }

      }else if(sort == 2){
        try {
          finalPackage = new SalesSorter(searchParameterOrigin, searchPackage, 1, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:2", searchParameterOrigin, null);
        }catch (Exception e){
          e.printStackTrace();
          logWarning("Sorting failed", searchParameterOrigin, e);
          finalPackage = Lists.newArrayList(searchPackage);
        }
      }else if(sort == 3){
        try {
          finalPackage = new PriceSorter(searchParameterOrigin, searchPackage, 0, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:3", searchParameterOrigin, null);
        }catch (Exception e){
          e.printStackTrace();
          logWarning("Sorting failed", searchParameterOrigin, e);
          finalPackage = Lists.newArrayList(searchPackage);
        }

      }else if(sort == 4){
        try {
          finalPackage = new PriceSorter(searchParameterOrigin, searchPackage, 1, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:4", searchParameterOrigin, null);
        }catch (Exception e){
          e.printStackTrace();
          logWarning("Sorting failed", searchParameterOrigin, e);
          finalPackage = Lists.newArrayList(searchPackage);
        }
      }
      //
      t6 = System.currentTimeMillis();
    }else{
      //error
      writeError(searchParameterOrigin, resp, beginTime, INVALID_PARAMETER, null);
      return;
    }

    /*if(sort == 0) {
      WarningLogger.logWarning("sort:0", searchParameterOrigin, null);
      if(meta_data){
        if (filters.size() == 0) {
          packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                    basic_data, placeSearchCensus, typeSearchCensus);
        } else {
          try {
            packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                      basic_data, filters, placeSearchCensus, typeSearchCensus);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }else {
        if (filters.size() == 0) {
          packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                    basic_data);
        } else {
          try {
            packages = packageByType1(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                      basic_data, filters);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }

      t5 = System.currentTimeMillis();
      transformMergeResultTime = t5 - t4;

      // 排序
      List<Type1SearchPackage> sortedPackages;
      try {
        sortedPackages = new Type1Sorter(searchParameterOrigin, packages).sortSearchPackages();
      } catch (SearchException e) {
        logWarning("Sorting failed", searchParameterOrigin, e);
        sortedPackages = packages;
      }

      t6 = System.currentTimeMillis();
      List<SKU> skuLists = new ArrayList<>();
      List<Type1SearchPackage> sortedPackages1 = new ArrayList<>();
      if (blendAll&& CollectionUtils.isNotEmpty(sortedPackages)) {
        //如果混排，将更符合用户意图的产品前置
        if(!preSKUType2.isEmpty())
          local = true;
        for(int i = 0; i < sortedPackages.size(); i++){
          Type1SearchPackage type1SearchPackage = sortedPackages.get(i);
          if(preSKUType1.contains(type1SearchPackage.getType1())){
            skuLists.addAll(type1SearchPackage.getSkus());
          }else if(local && (type1SearchPackage.getType1() == 2410)){
            boolean isEmpty = true;
            Type1SearchPackage type1SearchPackage1 = new Type1SearchPackage();
            List<SKU> _skus = type1SearchPackage.getSkus();
            for(SKU sku:_skus){
              if(preSKUType2.contains(sku.getType2()))
                skuLists.add(sku);
              else {
                type1SearchPackage1.addSku(sku);
                isEmpty = false;
              }
            }
            if(!isEmpty){
              type1SearchPackage1.setType1(2410);
              sortedPackages1.add(type1SearchPackage1);
            }
          }else {
            sortedPackages1.add(type1SearchPackage);
          }
        }
        //
        int preSize = skuLists.size();
        List<SKU> skus = null;
        SearchPackage blendedSearchPackage;
        if(limit != Integer.MAX_VALUE)
          blendedSearchPackage = new SearchPackage(limit);
        else
          blendedSearchPackage = new SearchPackage(1000);
        if(preSize > limit){
          skus = ListUtils.ToSubList(skuLists, 0, limit);
        }else if((preSize < limit)&&CollectionUtils.isNotEmpty(sortedPackages1)){
          sortedPackages = sortedPackages1;
          Blender<Integer, SKU> blender;
          Map<Integer, List<SKU>> map = Maps.newHashMapWithExpectedSize(sortedPackages.size());
          for (Type1SearchPackage type1SearchPackage : sortedPackages) {
            map.put(type1SearchPackage.getType1(), type1SearchPackage.getSkus());
          }
          try {
            if (StringUtils.isEmpty(blendString)) {
              blender = new Blender<>(DEFAULT_BLENDING_LAYOUT, map);
            } else {
              blender = new Blender<>(blendString, 3, new StringIntConverter(), map);
            }
            blender.blend();
          } catch (Exception e) {
            writeError(searchParameterOrigin, resp, beginTime, RESULT_PROCESSING_ERROR, e);
            return;
          }
          //List<SKU> skus = blender.getTotalList();
          skus = skuLists;
          skus.addAll(ListUtils.ToSubList(blender.getTotalList(), 0, limit - preSize));
        }else {
          skus = skuLists;
        }
        //
        //blendedSearchPackage.addSkus(skuLists);
        blendedSearchPackage.addSkus(skus);
        finalPackage = Lists.newArrayList(blendedSearchPackage);
      } else {
        if(limit == Integer.MAX_VALUE)
          finalPackage = sortedPackages;
        else{
          for(int i = 0; i < sortedPackages.size(); i++){
            sortedPackages.get(i).resize(limit);
          }
          finalPackage = sortedPackages;
        }
      }

    }else if(sort >= 1 && sort <= 4) {//按价格排序，从低到高
      //System.out.println("SORT:"+sort);
      if(meta_data){
        if (filters.size() == 0) {
          searchPackage = packageAll(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                     basic_data, placeSearchCensus, typeSearchCensus);
        } else {
          try {
            searchPackage = packageAll(searchParameterOrigin, searchResultStringList,
                                       useSimpleResult, basic_data, filters, placeSearchCensus, typeSearchCensus);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }else {
        if (filters.size() == 0) {
          searchPackage = packageAll(searchParameterOrigin, searchResultStringList, useSimpleResult,
                                     basic_data);
        } else {
          try {
            searchPackage = packageAll(searchParameterOrigin, searchResultStringList,
                                       useSimpleResult, basic_data, filters);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }

      //System.out.println("idSet:"+searchPackage.getIdSet());

      t5 = System.currentTimeMillis();
      t6 = t5;
      transformMergeResultTime = t5 - t4;
      if(sort == 1){
        try {
          finalPackage = new SalesSorter(searchParameterOrigin, searchPackage, 0, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:1", searchParameterOrigin, null);
        }catch (Exception e){
          logWarning("Sorting failed", searchParameterOrigin, e);
          e.printStackTrace();
          finalPackage = Lists.newArrayList(searchPackage);
        }

      }else if(sort == 2){
        try {
          finalPackage = new SalesSorter(searchParameterOrigin, searchPackage, 1, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:2", searchParameterOrigin, null);
        }catch (Exception e){
          e.printStackTrace();
          logWarning("Sorting failed", searchParameterOrigin, e);
          finalPackage = Lists.newArrayList(searchPackage);
        }
      }else if(sort == 3){
        try {
          finalPackage = new PriceSorter(searchParameterOrigin, searchPackage, 0, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:3", searchParameterOrigin, null);
        }catch (Exception e){
          e.printStackTrace();
          logWarning("Sorting failed", searchParameterOrigin, e);
          finalPackage = Lists.newArrayList(searchPackage);
        }

      }else if(sort == 4){
        try {
          finalPackage = new PriceSorter(searchParameterOrigin, searchPackage, 1, limit).sortSearchPackages();
          //System.out.println("after sort:"+finalPackage.get(0).getIdSet());
          WarningLogger.logWarning("sort:4", searchParameterOrigin, null);
        }catch (Exception e){
          e.printStackTrace();
          logWarning("Sorting failed", searchParameterOrigin, e);
          finalPackage = Lists.newArrayList(searchPackage);
        }
      }

    }*/

    t7 = System.currentTimeMillis();
    /*
    Class<?> c = Class.forName("java.nio.Bits");
    Field maxMemory = c.getDeclaredField("maxMemory");
    maxMemory.setAccessible(true);
    Field reservedMemory = c.getDeclaredField("reservedMemory");
    reservedMemory.setAccessible(true);
    Long maxMemoryValue = (Long)maxMemory.get(null);
    Long reservedMemoryValue = (Long)reservedMemory.get(null);
    */

    SearchReportItem sri = new SearchReportItem(searchParameterOrigin, finalPackage);
    TimeCost tc = new TimeCost(t2 - t1, t3 - t2, t4 - t3, transformMergeResultTime, t6 - t5,
                               t7 - beginTime).addTime("blend-result", t7 - t6);
    report(sri, tc);

    if (onlyData) {
      writeOK(resp, beginTime, searchParameterOrigin.getRequestId(), DEFAULT_STRA, finalPackage,
              metaInfo);
    } else {
      //临时增加
      if(basic_data)
        useSimpleResult = true;
      doRenderPage(resp, searchParameterOrigin, tuples, finalPackage, t5 - beginTime,
                   resp.getWriter(), useSimpleResult, blendAll, DEFAULT_BLENDING_LAYOUT,
                   startTimeShow, limitStr);
    }
  }

  private void doRenderPage(HttpServletResponse resp, SearchParameterOrigin searchParameter,
                            Set<List<Word>> tuples, List<? extends SearchPackage> packages,
                            long requestTime, Writer writer, boolean useSimpleResult,
                            boolean blendResult,
                            List<BlenderLayoutPattern<Integer>> blendLayouts, String startTimeShow, String limit) throws IOException {
    VMTemplate.getInstance().mergeSearchResult(searchParameter, tuples, packages, requestTime, this,
                                               resp.getWriter(), useSimpleResult, blendResult,
                                               blendLayouts, startTimeShow, limit);
    writer.flush();
  }
}
